package week9;

import jsjf.QueueADT;
import jsjf.StackADT;
import week2.LinkedStack;
import week3.LinkedQueue;
import week4.ArrayUnorderedList;
import week4.UnorderedListADT;

import java.util.*;

public class ListGraph<T> implements GraphADT<T> {
    protected int numVertices;    // 图中顶点的数量
    protected ArrayList<VerticeNode<T>> adjList;   // 邻接列表
    protected T[] vertices;    // 顶点的值
    protected int modCount;

    public ListGraph() {
        numVertices = 0;
        adjList = new ArrayList<VerticeNode<T>>();
    }

    public String toString()
    {
        String result = "";
        while (!adjList.isEmpty())
        {
            for (int x = 0;x < adjList.size();x++)
            {
                VerticeNode node = adjList.get(x);
                System.out.print(node.element + " ");
                while (node.next != null)
                {
                    System.out.println(node.next.element + " ");
                    node = node.next;
                }
            }
        }
        return result;
    }


    @Override
    public void addVertex(T vertex) {
        VerticeNode node = new VerticeNode(vertex);
        adjList.add(node);

        numVertices++;
        modCount++;
    }

    @Override
    public void removeVertex(T vertex) {
        VerticeNode node = new VerticeNode(vertex);
        adjList.remove(node);

        numVertices--;
        modCount++;
    }

    public int getIndex(T vertex)
    {
        int index = -1;

        VerticeNode node = new VerticeNode(vertex);

        for (int x = 0; x < numVertices; x++ )
        {
            if (vertex.equals(adjList.get(x).element))
            {
                index = x;
            }
        }
        return index;
    }

    @Override
    public void addEdge(T vertex1, T vertex2) {
        if (getIndex(vertex1) != -1 && getIndex(vertex2) != -1) {
            VerticeNode<T> vertexNode1 = adjList.get(getIndex(vertex1));
            VerticeNode<T> vertexNode2 = adjList.get(getIndex(vertex2));
            VerticeNode<T> temp1 = new VerticeNode<>(vertex1);
            VerticeNode<T> temp2 = new VerticeNode<>(vertex2);
            while (vertexNode1.next != null)
                vertexNode1 = vertexNode1.next;
            vertexNode1.next = temp2;
            while (vertexNode2.next != null)
                vertexNode2 = vertexNode2.next;
            vertexNode2.next = temp1;

        }
    }

    @Override
    public void removeEdge(T vertex1, T vertex2) {
        if(getIndex(vertex1)!=-1&&getIndex(vertex2)!=-1){
            VerticeNode<T> vertexNode1 = adjList.get(getIndex(vertex1));
            VerticeNode<T> vertexNode2 = adjList.get(getIndex(vertex2));
            VerticeNode<T> temp1 = null,temp2 = null;

            while(vertexNode1.element != vertex2 ) {
                temp1 = vertexNode1;
                vertexNode1 = vertexNode1.next;
            }
            if(vertexNode1.next==null)
                temp1.next = null;
            else{
                VerticeNode<T> temp = vertexNode1.next;
                temp1.next = temp;
            }


            while(vertexNode2.element != vertex1  ) {
                temp2 = vertexNode2;
                vertexNode2 = vertexNode2.next;
            }
            if(vertexNode2.next==null)
                temp2.next = null;
            else{
                VerticeNode<T> temp = vertexNode2.next;
                temp2.next = temp;
            }
        }
    }

    @Override
    public Iterator iteratorBFS(T startVertex) {
        return iteratorBFS(getIndex(startVertex));
    }

    public Iterator<T> iteratorBFS(int startIndex)
    {
        Integer x;
        QueueADT<Integer> traversalQueue = new LinkedQueue<Integer>();
        UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();

        if (!indexIsValid(startIndex))
            return resultList.iterator();

        boolean[] visited = new boolean[numVertices];
        for (int i = 0; i < numVertices; i++)
            visited[i] = false;

        traversalQueue.enqueue(startIndex);
        visited[startIndex] = true;

        while (!traversalQueue.isEmpty())
        {
            x = traversalQueue.dequeue();
            resultList.addToRear((T)adjList.get(x).element);

            //Find all vertices adjacent to x that have not been visited
            //     and queue them up

            for (int i = 0; i < numVertices; i++)
            {
                if (isEdge(x,i) && !visited[i])
                {
                    traversalQueue.enqueue(i);
                    visited[i] = true;
                }

            }
        }
        return new GraphIterator(resultList.iterator());
    }

    private boolean indexIsValid(int index) {
        if (index<numVertices)
            return true;
        else
            return false;
    }

    public boolean isEdge(int i, int j){
        if(i==j)
            return false;
        VerticeNode vertexNode1 = adjList.get(i);
        VerticeNode vertexNode2 = adjList.get(j);
        while(vertexNode1!=null) {
            if (vertexNode1.element == vertexNode2.element)
                return true;
            vertexNode1 =vertexNode1.next;
        }
        return false;
    }

    @Override
    public Iterator iteratorDFS(T startVertex) {
        return iteratorDFS(getIndex(startVertex));
    }

    public Iterator<T> iteratorDFS(int startIndex)
    {
        Integer x;
        boolean found;
        this.vertices = (T[])(new Object[numVertices]);
        StackADT<Integer> traversalStack = new LinkedStack<Integer>();
        UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();
        boolean[] visited = new boolean[numVertices];

        if (!indexIsValid(startIndex))
            return resultList.iterator();

        for (int i = 0; i < numVertices; i++)
            visited[i] = false;

        traversalStack.push(startIndex);
        resultList.addToRear((T)adjList.get(startIndex).element);
        visited[startIndex] = true;

        while (!traversalStack.isEmpty())
        {
            x = traversalStack.peek();
            found = false;

            //Find a vertex adjacent to x that has not been visited
            //     and push it on the stack
            for (int i = 0; (i < numVertices) && !found; i++)
            {
                if (isEdge(x,i) && !visited[i])
                {
                    traversalStack.push(i);
                    resultList.addToRear((T)adjList.get(i).element);
                    visited[i] = true;
                    found = true;
                }
            }
            if (!found && !traversalStack.isEmpty())
                traversalStack.pop();
        }
        return new GraphIterator(resultList.iterator());
    }

    @Override
    public Iterator iteratorShortestPath(T startVertex, T targetVertex) {
        return iteratorShortestPath(getIndex(startVertex), getIndex(targetVertex));
    }

    public Iterator<T> iteratorShortestPath(int startIndex, int targetIndex)
    {
        UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();
        if (!indexIsValid(startIndex) || !indexIsValid(targetIndex))
            return resultList.iterator();

        Iterator<Integer> it = iteratorShortestPathIndices(startIndex,
                targetIndex);
        while (it.hasNext())
            resultList.addToRear((T)adjList.get(((Integer)it.next())).element);
        return new GraphIterator(resultList.iterator());
    }


    protected Iterator<Integer> iteratorShortestPathIndices
            (int startIndex, int targetIndex)
    {
        int index = startIndex;
        int[] pathLength = new int[numVertices];
        int[] predecessor = new int[numVertices];
        QueueADT<Integer> traversalQueue = new LinkedQueue<Integer>();
        UnorderedListADT<Integer> resultList =
                new ArrayUnorderedList<Integer>();

        if (!indexIsValid(startIndex) || !indexIsValid(targetIndex) ||
                (startIndex == targetIndex))
            return resultList.iterator();

        boolean[] visited = new boolean[numVertices];
        for (int i = 0; i < numVertices; i++)
            visited[i] = false;

        traversalQueue.enqueue(Integer.valueOf(startIndex));
        visited[startIndex] = true;
        pathLength[startIndex] = 0;
        predecessor[startIndex] = -1;

        while (!traversalQueue.isEmpty() && (index != targetIndex))
        {
            index = (traversalQueue.dequeue()).intValue();

            //Update the pathLength for each unvisited vertex adjacent
            //     to the vertex at the current index.
            for (int i = 0; i < numVertices; i++)
            {
                if (isEdge(index,i) && !visited[i])
                {
                    pathLength[i] = pathLength[index] + 1;
                    predecessor[i] = index;
                    traversalQueue.enqueue(Integer.valueOf(i));
                    visited[i] = true;
                }
            }
        }
        if (index != targetIndex)  // no path must have been found
            return resultList.iterator();

        StackADT<Integer> stack = new LinkedStack<Integer>();
        index = targetIndex;
        stack.push(Integer.valueOf(index));
        do
        {
            index = predecessor[index];
            stack.push(Integer.valueOf(index));
        } while (index != startIndex);

        while (!stack.isEmpty())
            resultList.addToRear(((Integer)stack.pop()));

        return new GraphIndexIterator(resultList.iterator());
    }

    @Override
    public boolean isEmpty() {
        return numVertices == 0;
    }

    @Override
    public boolean isConnected() {
        boolean result = true;
        for(int i=0;i<numVertices;i++){
            int temp=0;
            temp=getSizeOfIterator(iteratorBFS(i));
            if(temp!=numVertices)
            {
                result = false;
                break;
            }
        }
        return result;
    }

    private int getSizeOfIterator(Iterator iterator) {
        int size = 0;
        while(iterator.hasNext()){
            size++;
            iterator.next();
        }
        return size;
    }

    @Override
    public int size() {
        return numVertices;
    }

    public class GraphIterator implements Iterator<T>
    {
        private int expectedModCount;
        private Iterator<T> iter;


        public GraphIterator(Iterator<T> iter)
        {
            this.iter = iter;
            expectedModCount = modCount;
        }



        public boolean hasNext() throws ConcurrentModificationException
        {
            if (!(modCount == expectedModCount))
                throw new ConcurrentModificationException();

            return (iter.hasNext());
        }


        public T next() throws NoSuchElementException
        {
            if (hasNext())
                return (iter.next());
            else
                throw new NoSuchElementException();
        }


        public void remove()
        {
            throw new UnsupportedOperationException();
        }
    }


    public class GraphIndexIterator implements Iterator<Integer>
    {
        private int expectedModCount;
        private Iterator<Integer> iter;


        public GraphIndexIterator(Iterator<Integer> iter)
        {
            this.iter = iter;
            expectedModCount = modCount;
        }


        public boolean hasNext() throws ConcurrentModificationException
        {
            if (!(modCount == expectedModCount))
                throw new ConcurrentModificationException();

            return (iter.hasNext());
        }


        public Integer next() throws NoSuchElementException
        {
            if (hasNext())
                return (iter.next());
            else
                throw new NoSuchElementException();
        }


        public void remove()
        {
            throw new UnsupportedOperationException();
        }
    }
}
